package com.baitiaojun.gateway.filter;

import com.baitiaojun.api.util.ConstantUtil;
import com.baitiaojun.api.util.TokenUtil;
import com.baitiaojun.api.vo.JsonRes;
import com.baitiaojun.gateway.support.FilterSupport;
import com.baitiaojun.gateway.support.RedisSupport;
import lombok.Setter;
import lombok.SneakyThrows;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @类名 LoginCheckFilter
 * @作者 白条君
 * @创建日期 2022/10/26 16:56
 * @版本 1.0
 */
@Setter
@Order(1)
@Component
@ConfigurationProperties(prefix = "public")
public class AccessCheckFilter implements GlobalFilter {

    private List<String> pathList;

    @Resource
    private RedisSupport redisSupport;

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        //第一次登录接口以及访问未登录情况下允许访问的接口
        String reqPath = exchange.getRequest().getURI().getPath();
        if (pathList.stream().anyMatch(path -> StringUtils.pathEquals(path, reqPath))) {
            return chain.filter(exchange);
        }
        //如果不是第一次登录且访问不允许未登录情况下访问的接口
        List<String> authorization = exchange.getRequest().getHeaders().get("Authorization");
        //校验请求头的authorization字段中是否存在的accessToken
        String accessToken;
        if (ObjectUtils.isEmpty(authorization)) {
            return FilterSupport.response(exchange, JsonRes.builder().error().data("无法获取token"));
        } else {
            accessToken = authorization.get(0);
            if (StringUtils.isEmpty(accessToken)) {
                return FilterSupport.response(exchange, JsonRes.builder().error().data("无法获取token"));
            }
        }
        //校验当前access-token的用户缓存信息是否过期
        Boolean hasToken = redisSupport.getRedisTemplate().hasKey(accessToken);
        if (Boolean.FALSE.equals(hasToken)) {
            return FilterSupport.response(exchange, JsonRes.builder().error().data("不存在此token的用户缓存数据"));
        }
        //校验当前accessToken是否过期，没有过期就直接放行请求，过期就重新获取access-token
        if (TokenUtil.isExpiresToken(accessToken)) {
            handleMultipart(request);
            return chain.filter(exchange);
        }
        //校验当前refreshToken是否过期，没过期就重新进行accesstoken获取，否则就说明过期，用户需要重新登录，
        String refreshToken = String.valueOf(redisSupport.getHash(accessToken, ConstantUtil.Field.REFRESH_TOKEN.getKey()));
        if (TokenUtil.isExpiresToken(refreshToken)) {
            Map<String, Object> values = redisSupport.hash().entries(accessToken);
            redisSupport.delete(accessToken);
            accessToken = TokenUtil.generateToken(TokenUtil.verifyToken(refreshToken), ConstantUtil.TokenType.ACCESS);
            redisSupport.hash().putAll(accessToken, values);
            redisSupport.getRedisTemplate().expire(accessToken, ConstantUtil.REFRESH_TOKEN_TIMEOUT, TimeUnit.DAYS);
            return FilterSupport.response(exchange, accessToken);
        } else {
            return FilterSupport.response(exchange, JsonRes.builder().error().data("无法访问，需要重新登录获取有效token令牌"));
        }
    }

    private void handleMultipart(ServerHttpRequest request) {
        String path = request.getURI().getPath();
        if ("/moments/upload-moments-pictures".equals(path)) {
            String contentType = Objects.requireNonNull(request.getHeaders().get("Content-Type")).get(0);
            request.getHeaders().set("Content-Type", contentType);
        }
    }
}